Skip to content

fix(maintenance): import DEFAULT variant so experiment starter restore works (#36337)#36362

Merged
erickgonzalez merged 2 commits into
mainfrom
issue-36337-fix-import-experiments-variants
Jun 30, 2026
Merged

fix(maintenance): import DEFAULT variant so experiment starter restore works (#36337)#36362
erickgonzalez merged 2 commits into
mainfrom
issue-36337-fix-import-experiments-variants

Conversation

@erickgonzalez

@erickgonzalez erickgonzalez commented Jun 29, 2026

Copy link
Copy Markdown
Member

Proposed Changes

Follow-up to #36339 (export/import of Experiments and Variants in the starter).

Manual round-trip testing surfaced a fatal error when importing a starter that contains experiments — startup aborts with:

Cannot construct instance of `com.dotcms.experiments.model.TrafficProportion`,
problem: Invalid Variants provided

Root cause

On a fresh install the starter import runs in MainServlet.init() at line 129 (executeStartUpTasks()Task00004LoadStarterImportStarterUtil), while the DEFAULT variant is created later at line 218 (DotInitializationServiceDefaultVariantInitializer). postgres.sql does not seed the DEFAULT variant row, and the run-once upgrade tasks don't run on this path.

Every experiment's trafficProportion references the DEFAULT variant. Building the Experiment immutable during import triggers AbstractTrafficProportion's @Value.Check, which validates that every referenced variant exists via VariantAPI.get(...). Because the import branch was explicitly skipping the DEFAULT variant, it was absent at deserialize time → validation fails → startup is aborted.

Fix

Stop skipping DEFAULT in the variant import branch — import it from the starter like any other variant. The existing variantFactory.get(name).isPresent() guard still prevents PK duplicates and makes it a no-op when the initializer already created the row. With DEFAULT (and the experiment variants) present before experiments deserialize, the model builds and ExperimentsFactory.save() works as designed.

This keeps the factory-based import (no raw SQL) and changes only ImportStarterUtil.

Also included

  • fix(docker): bump CUSTOM_STARTER_URL references to the latest starter version (20260629) across docker-compose examples and docs, so deployments initialize from a starter that exercises this path.

How to Test

  1. On an instance with at least one A/B Experiment, download a starter (_downloadStarter / _downloadStarterWithAssets).
  2. Point a fresh install at that starter (e.g. CUSTOM_STARTER_URL) and boot it.
  3. Startup completes (no Invalid Variants provided), and the experiment + its variants are present and open correctly in the UI.

🤖 Generated with Claude Code

This PR fixes: #36337

This PR fixes: #36337

erickgonzalez and others added 2 commits June 29, 2026 16:00
… validation (#36337)

On a fresh install the starter import (Task00004LoadStarter, MainServlet line 129)
runs before DefaultVariantInitializer (line 218), and postgres.sql does not seed
the DEFAULT variant row. Every experiment's trafficProportion references the
DEFAULT variant, so building the Experiment model during import triggers
AbstractTrafficProportion's @Value.Check, which fails variant-existence validation
("Invalid Variants provided") and aborts startup.

The variant import branch was explicitly skipping DEFAULT. Import it from the
starter instead (the existence guard still prevents PK duplicates and is a no-op
when the initializer already created it). With DEFAULT and the experiment variants
present before experiments deserialize, the model builds and ExperimentsFactory.save
works as intended.

Follow-up to #36339.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… version**

- Updated all occurrences of `CUSTOM_STARTER_URL` in `docker-compose.yml`, `README.md`, and related files to reference the latest starter version (`20260629`).
- Ensured consistency across various environments, including single-node, cluster-mode, Redis, OpenSearch, push-publish, and manual setup examples.
- Adjusted sample configuration files and documentation to reflect the updated URL for better initialization during deployments.

ref: #36337
Signed-off-by: erickgonzalez <erick.gonzalez@dotcms.com>
@github-actions github-actions Bot added Area : Backend PR changes Java/Maven backend code Area : CLI PR changes dotCMS CLI code Area : Documentation PR changes documentation files labels Jun 29, 2026
@claude

claude Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @erickgonzalez's task in 1m 7s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get full PR diff
  • Analyze diff against all unsafe categories
  • Apply appropriate label and post findings

Result: ✅ Safe to Roll Back

The label AI: Safe To Rollback has been applied.


Analysis

The PR contains two logical changes. Neither matches any rollback-unsafe category.

1. ImportStarterUtil.java — Stop skipping DEFAULT variant during starter import

The change removes a special-case VariantAPI.DEFAULT_VARIANT.name().equals(v.name()) guard from the import loop and relies solely on the variantFactory.get(v.name()).isPresent() idempotency check instead (dotCMS/src/main/java/com/dotmarketing/util/starter/ImportStarterUtil.java, lines 603–614).

  • No DB schema change — no ALTER TABLE, ADD COLUMN, DROP COLUMN, or runonce task.
  • No data model version bumpImmutableContentlet.CURRENT_MODEL_VERSION is untouched.
  • No ES mapping change — no changes to ESMappingAPIImpl, ESMappingConstants, or any field type mapping.
  • No PK restructuring — the variant table's primary key is unchanged; the existing variantFactory.save() path is used with the pre-existing get().isPresent() duplicate guard, so it is fully idempotent.
  • Rollback behavior: If rolled back to N-1, the old code resumes skipping the DEFAULT variant during import. A fresh install with an experiment-containing starter would revert to the startup-abort bug that this PR fixes — but that is a forward-compatibility issue (N-1 broken again), not a rollback safety issue. N-1 database and index state are unaffected.

2. CUSTOM_STARTER_URL / DOTCMS_STARTER_URL bumps (20+ docker-compose / README files)

All changes are documentation and configuration reference updates — URLs in comments or example YAML files pointing at a newer starter artifact (20260629). These carry no runtime storage contract changes whatsoever.

Quick-reference check against all categories:

Category Match?
C-1 Structural Data Model Change ✅ No
C-2 ES Mapping Change ✅ No
C-3 Content JSON Model Version Bump ✅ No
C-4 DROP TABLE / DROP COLUMN ✅ No
H-1 One-Way Data Migration ✅ No
H-2 RENAME TABLE / COLUMN ✅ No
H-3 PK Restructuring ✅ No
H-4 New ContentType Field Type ✅ No
H-5 Storage Provider Change ✅ No
H-6 DROP PROCEDURE / FUNCTION ✅ No
H-7 NOT NULL Column Without Default ✅ No
H-8 VTL Viewtool Contract Change ✅ No
M-1 Non-Broadening Column Type Change ✅ No
M-2 Push Publishing Bundle Format Change ✅ No
M-3 REST / GraphQL API Contract Change ✅ No
M-4 OSGi Plugin API Breakage ✅ No

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🤖 dotBot Review (Bedrock)

Reviewed 26 file(s); 2 candidate(s) → 2 confirmed, 0 uncertain (unverified, kept for review).

Confirmed findings

  • 🟠 High dotBackendOnboarding.md:198 — Starter URL uses incorrect future date
    The URL in dotBackendOnboarding.md references 20260629 which appears to be a typo for 20240629. This date format (YYYYMMDD) suggests June 29, 2026 - 2 years in the future from current date - making the starter file unavailable and breaking fresh installs using CUSTOM_STARTER_URL.
  • 🟡 Medium dotCMS/src/docker-compose/local-run/docker-compose.yml:56 — Potential future-dated starter URL in example
    The commented CUSTOM_STARTER_URL example uses 20260629 which appears to be a typo (2 years future-dated from original 2024 examples). While commented, this could mislead users into configuring an invalid/nonexistent starter URL. PR context mentions updating to 'latest starter version' which would logically use a 2024 date, making 2026 likely erroneous.

us.deepseek.r1-v1:0 · Run: #28405946285 · tokens: in: 65646 · out: 13496 · total: 79142 · calls: 32 · est. ~$0.162

@erickgonzalez

Copy link
Copy Markdown
Member Author

@claude create a description for this PR

@claude

claude Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @erickgonzalez's task in 39s —— View job


Tasks

  • Read changed files for context
  • Craft PR description

PR Description

fix(maintenance): import DEFAULT variant so experiment starter restore works

Summary

Fixes a fatal startup error when restoring a starter that contains A/B experiments. Before this fix, booting from such a starter would abort with:

Cannot construct instance of `com.dotcms.experiments.model.TrafficProportion`,
problem: Invalid Variants provided

Root Cause

There is an initialization ordering gap in MainServlet.init():

Line Step
129 executeStartUpTasks()Task00004LoadStarterImportStarterUtil runs
218 DotInitializationServiceDefaultVariantInitializer seeds the DEFAULT variant

Every experiment's trafficProportion holds a reference to the DEFAULT variant. When ImportStarterUtil deserializes an Experiment, AbstractTrafficProportion's @Value.Check immediately validates that every referenced variant exists via VariantAPI.get(...). Because the old import code was explicitly skipping the DEFAULT variant and postgres.sql does not seed it, the DEFAULT variant was absent at deserialize time — validation failed and startup was aborted.

Fix

Stop skipping DEFAULT in the variant import branch — import it from the starter like any other variant (ImportStarterUtil.java, variant block, lines 599–615). The existing variantFactory.get(v.name()).isPresent() guard already prevents PK violations and makes the upsert a no-op when DefaultVariantInitializer has already run. With DEFAULT (and all experiment variants) present before experiments are deserialized, AbstractTrafficProportion validates successfully and ExperimentsFactory.save() completes as designed.

The fix stays entirely within ImportStarterUtil and uses the factory layer (no raw SQL).

Also Included

Bumps CUSTOM_STARTER_URL references to the latest starter version (20260629) across all docker-compose examples, documentation, and CLI test resources. This ensures new deployments initialize from a starter that exercises the fixed code path.

Changed Files

File Change
dotCMS/src/main/java/com/dotmarketing/util/starter/ImportStarterUtil.java Core fix — remove DEFAULT variant skip, rely on presence-check guard
docker/docker-compose-examples/** (×17 files) Bump CUSTOM_STARTER_URL to 20260629
docker/dev-env/README.md, dotBackendOnboarding.md Bump starter version in docs
tools/dotcms-cli/**/docker-compose.yaml (×2 files) Bump starter version in CLI test resources

How to Test

  1. On an instance with at least one A/B Experiment, generate a starter via _downloadStarter or _downloadStarterWithAssets.
  2. Point a fresh install at that starter (e.g. via CUSTOM_STARTER_URL) and start dotCMS.
  3. Before fix: startup aborts with Invalid Variants provided.
  4. After fix: startup completes, experiments and their variants are present and open correctly in the UI.

Comment thread dotBackendOnboarding.md
@erickgonzalez erickgonzalez enabled auto-merge June 29, 2026 22:18
@erickgonzalez erickgonzalez added this pull request to the merge queue Jun 29, 2026
@mergify

mergify Bot commented Jun 29, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Merged via the queue into main with commit fd8ac59 Jun 30, 2026
61 of 62 checks passed
@erickgonzalez erickgonzalez deleted the issue-36337-fix-import-experiments-variants branch June 30, 2026 00:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code Area : CLI PR changes dotCMS CLI code Area : Documentation PR changes documentation files

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Starter export/import omits Experiments and Variants data

3 participants